package langnstats.project.languagemodel.hmm;

import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Random;

import langnstats.project.Global;
import langnstats.project.lib.LanguageModel;
import langnstats.project.lib.WordType;
import langnstats.project.lib.crossvalidation.CrossValidationResult;
import langnstats.project.lib.crossvalidation.CrossValidationResultItem;
import langnstats.project.lib.crossvalidation.CrossValidationTokenSet;
import langnstats.project.lib.crossvalidation.TrainTokens;
import langnstats.project.tools.Logdouble;

public class HMM implements LanguageModel{
	private static final long serialVersionUID = 1L;
	private double[][] a;
	private double[][] b;
	
	private static double[][] getInitialA(int size){
		double[][] returnA = null;
		if(size==2){
			returnA = new double[][]{
					{0.3954037697354323, 0.6045962302645677}, 
					{0.8471400537952428, 0.1528599462047572}
					};
		}
		else if(size==3){
			returnA = new double[][]{
					{0.4296517398479734, 0.1794167890420209, 0.39093147111000576}, 
					{0.3935914661211002, 0.21079639375449288, 0.3956121401244069}, 
					{0.5794608264103948, 0.072085452577676, 0.3484537210119293}
					};
		}
		else if(size==20){
			returnA =  new double[][] {
					{0.017188945199961718, 0.41002095856428733, 1.5938798335997657E-4, 9.770352981572762E-4, 0.08551811317561746, 0.0038155742471634767, 0.0020823677865239483, 9.78351270624718E-5, 3.256250733228502E-8, 4.480439495393302E-4, 0.023771266147434075, 0.0030229192970715676, 0.01308475174423877, 0.11836186391552236, 0.0770710204433654, 0.04114094587587684, 0.05171475880666988, 0.13900436646407263, 0.012518400635553376, 1.412776014618275E-6}, 
					{0.007219151249632085, 0.0010570518149164787, 5.380049326047825E-16, 2.5399482391827494E-5, 0.0031505001126428188, 0.02716133425164341, 0.002293790375684089, 8.01814388904918E-7, 6.884996454712136E-8, 1.1411107983945039E-4, 0.012403283449346075, 4.4915647314321713E-4, 0.0034387229605173423, 0.0818698659732884, 0.001912109670553206, 3.3800436179207973E-4, 0.5326180676040594, 0.01238719768055534, 0.3135305526809335, 3.083011470735591E-5}, 
					{1.875351166455909E-4, 0.018611228601345026, 0.008687808099343287, 4.544066493072091E-11, 0.005384023545055257, 6.95506375380093E-8, 4.99284523575846E-4, 2.6220084358225797E-11, 4.4955797700328666E-10, 0.8328489110825873, 3.1137285139875393E-4, 6.953142033191141E-4, 7.631789341041308E-6, 6.684860538322658E-4, 0.05671672275673149, 4.15240706670996E-12, 1.910092812264634E-6, 0.07534935212573877, 2.7173782982923524E-6, 2.763170396710971E-5}, 
					{0.11192753653977583, 0.18024557894701096, 0.07478387996866938, 0.0024348414417895143, 0.0032066288809812498, 1.901312393062951E-4, 0.05669640226151327, 7.62779714133649E-4, 4.252919991020751E-5, 0.002101311346448279, 0.0025480421263095908, 0.36207547588967687, 0.019387817639558606, 0.04427136680521722, 0.03486700701992449, 0.039146827996580875, 0.005899759564946017, 0.05133621997553091, 0.002990102521719914, 0.0050857609209966235}, 
					{0.33922559343614206, 0.030550498010434086, 0.04038726435007753, 0.017848411915917208, 0.002241869256877191, 4.211972210808311E-4, 0.35643604327276907, 0.00729166743663238, 0.07415442618830813, 5.07948930823686E-4, 0.006715707662117549, 0.0463977325084322, 0.015307178724241597, 0.004743550146043907, 1.1427548392547272E-5, 0.0023734771646078184, 7.681782654956059E-8, 0.005204774526740499, 0.0011775921856724831, 0.0490035626968624}, 
					{0.0651333600148631, 2.5373693437149086E-8, 0.0015345178341603044, 0.5712112187238514, 1.4937257214616823E-6, 6.0939650634069015E-6, 0.009133630235787664, 0.09112314392807314, 0.0666685443757559, 0.011997375027827401, 6.118197807364934E-4, 0.051850309090982025, 3.765629328054715E-5, 8.367901415585512E-7, 9.248014583974872E-7, 0.01880007056465515, 4.1979994822556634E-8, 1.274619537255239E-5, 6.571618399834994E-7, 0.11187553413674123}, 
					{0.17320290181335465, 0.21518784207883726, 0.0061676344396902035, 0.0029296363547267655, 5.971218909855856E-4, 2.792285734888246E-4, 3.107187120661842E-10, 1.728373519232791E-4, 1.3652280803794353E-4, 0.0035815020780167422, 0.1890868909420828, 0.012963763931836588, 0.04029162358250268, 0.16434145513153114, 1.5995957379471933E-4, 0.18343392520923793, 5.043994157500667E-5, 0.0013278999982737874, 0.006088583329007988, 2.3066037766033851E-7}, 
					{2.197318846087545E-7, 6.099205458425537E-5, 0.1759755251747934, 8.321160436036316E-4, 1.1047082829766699E-4, 7.86172539422719E-11, 0.0028505556821113455, 3.7249605759705965E-12, 2.0737473729233548E-11, 0.8184252028037658, 1.1789876332054352E-9, 9.179516188426973E-7, 1.6106222456231014E-11, 2.658566544566694E-5, 5.477141271588376E-14, 2.8887182171276E-5, 2.809687121208784E-8, 1.7662367269769757E-11, 4.280510140997758E-12, 0.0016884974646808567}, 
					{0.005605210531799123, 0.0883835984218372, 3.016351818653326E-4, 1.4789342004576059E-4, 0.051048433159746574, 0.023523390179844762, 1.9279149227078427E-5, 3.3928235094366583E-4, 2.8295452811803636E-7, 2.115387723235899E-8, 0.012785727698421031, 5.416728992399971E-4, 0.022913523133824686, 0.3006887044050912, 0.03222192588116831, 0.007279752508928315, 0.2174122546979433, 0.08029152379477139, 0.15649506763390206, 8.208429947191498E-7}, 
					{0.1398856166292208, 0.27420394700195083, 0.048317104127831995, 0.0024338147673928735, 0.04033395560037774, 1.6385622417135434E-4, 0.03971453613518857, 5.678366805638475E-4, 9.607119717118705E-7, 0.005025501551326513, 0.0011611826039959609, 0.2682775032230912, 0.018755040164149687, 0.023094863253635398, 0.004722854151809886, 0.00978874435102744, 0.01911935938860929, 0.09007981617717048, 0.009210194301796369, 0.005143312954718107}, 
					{0.18638653808671501, 0.11660954931262417, 0.006242004780266594, 1.3199052439218917E-5, 8.185065848508296E-5, 0.015475728532414107, 0.11956226688763165, 3.8572503223388796E-6, 0.007722691552410433, 0.0028875061303415906, 0.014277302135892107, 0.12759676419397967, 0.04111541043912992, 0.030996599659782742, 0.08740541223129573, 0.09374127424755009, 3.455049974059533E-6, 0.07061254810158468, 0.05925544403054973, 0.020010597666611077}, 
					{0.3532690972351184, 0.07794823531159827, 0.09802460905046906, 0.0022778367075648428, 6.59598117969207E-4, 2.8289458770920206E-5, 0.11726600632703724, 5.274423842548769E-4, 5.178655094775497E-4, 7.589821565953611E-4, 0.013055328151005817, 0.21394186802596046, 0.05024109199961549, 0.008263363688105103, 0.0038587722159743897, 0.0039678106584240536, 1.2189206521976876E-6, 0.03611775090386422, 0.005240908751137327, 0.01403392442640532}, 
					{0.4162461659795163, 5.3831774031312786E-9, 0.006431587285894087, 0.004246925287367516, 0.007950511033725271, 0.2518084758557417, 0.14326912515815785, 0.0012702597050752806, 0.06719027450940304, 2.0172874219672798E-8, 4.352100339433936E-4, 1.139197582299315E-5, 0.026610915324903738, 0.018502693323837742, 2.457939683849085E-7, 0.0032444369444186963, 0.021058708605764805, 7.043958568641557E-4, 6.675396637111636E-4, 0.03035111210583238}, 
					{1.672158092344489E-5, 0.0018321801352090333, 0.004882598989282402, 0.10827261968656707, 0.006908401932731655, 0.005617853084676447, 0.128422321115427, 0.01269228419604447, 0.21453483819514393, 7.978575489886368E-4, 4.4139094033100844E-5, 0.0016169666450891681, 1.2553510990817283E-5, 0.4477001670564074, 0.0014752664213097354, 0.0012641519859238042, 0.019044590052616973, 0.0011126153698650684, 0.0059881097704649324, 0.037763763628304796}, 
					{0.06463373135424783, 0.16126751209454146, 0.04440274568588519, 0.006699734536429339, 0.39978482040715, 7.764357041328E-5, 0.09078771511582462, 8.381971361124912E-4, 0.015604598494844146, 1.5045004168367887E-5, 0.01442944215531778, 7.587900102542253E-5, 0.0033938459216572106, 0.004412696120018915, 0.0010482009081445938, 3.646815089919919E-4, 0.0012072178117080324, 0.18929082294149807, 1.94235521722978E-5, 0.0016460466798490139}, 
					{0.0016261639956484995, 0.0027984928151407067, 1.2614870021295725E-4, 0.7629211228107972, 1.605665883171454E-11, 5.303896116370085E-7, 0.011970809168431615, 0.13106125913842384, 0.006457844393147975, 0.0026340361805679284, 1.492779483841679E-6, 1.3228806044835891E-5, 6.565904310124436E-10, 9.953234911725727E-5, 8.821941854110811E-12, 0.02407870312822754, 1.528762545517833E-14, 5.94179477561532E-12, 2.6402931912002617E-12, 0.05621063465507784}, 
					{0.27734745948233314, 0.005926723612371508, 0.024355181733227642, 0.11662232388010269, 0.0061057627006702465, 0.011132085693272831, 0.18769142917069126, 0.02571605098516163, 0.10177668539646718, 0.0017392331700730008, 0.0050443128848817, 0.02677378308971883, 0.011537276686700619, 0.005932248641365128, 2.976196064253982E-7, 0.00930355003601786, 0.1283433404944935, 8.591335443324194E-6, 0.0023030378324706297, 0.05234062555493072}, 
					{0.02808783311734884, 2.082643665942853E-6, 0.0031785955763032146, 6.484005088946694E-7, 0.7792514676368596, 0.009036730014483095, 0.04100415618044354, 3.8409366477983237E-7, 0.004576541868380723, 2.086800689849214E-9, 2.60822275059434E-4, 3.6596298562271456E-11, 0.002047829102948452, 0.001934754525406171, 8.962416485073534E-5, 4.0015115624852346E-5, 0.003127781389938459, 0.12261750567328353, 1.8405515899550816E-4, 0.004559170938837239}, 
					{0.0037798094982544076, 3.397065620416047E-14, 9.254458569033613E-4, 4.3106545540572036E-4, 0.021265487929209355, 0.03811128700378943, 0.002612312552273931, 4.6219894820493827E-4, 0.018767674824839542, 5.5417865014678404E-5, 0.0014725448688875787, 2.3775860709188977E-11, 0.0014215135664990886, 0.022264207670128495, 8.194979634216034E-6, 9.582471443208302E-4, 0.7268536485102574, 0.04704173059217556, 0.11349281661458147, 7.639609581029017E-5}, 
					{0.0105480805686556, 0.0011491760999337893, 0.0022551457133678275, 0.7596006584098671, 2.8734449695178083E-7, 3.7038916535633556E-6, 1.331208566020235E-4, 0.08422703753349753, 0.004838037008677084, 2.3465141008911314E-5, 1.7609406053969894E-4, 0.08762800311102098, 0.0032659218400622665, 5.460157048389905E-4, 8.655537157998792E-5, 0.015540294265345384, 1.1632357846268613E-7, 5.033289251751807E-4, 2.3190921599361553E-5, 0.029451766908499086}
					};
		}
		if(returnA!=null){
			if(returnA.length!=size){
				throw new RuntimeException("length of returnA("+returnA.length
						+") differnent from size input("+size+")");
			}
		}
		return returnA;
	}
	private static double[][] getInitialB(int size){
		double[][] returnB = null;
		if(size==2){
			returnB = new double[][]{
					{5.52451323958091E-4, 0.00442519412785825, 4.3682724830806837E-4, 0.0017490655116950407, 0.0025383822490718387, 0.01789647633229347, 0.051641441750233774, 0.14575974376040826, 0.0016461074943271683, 0.0051784456834640296, 0.036166597517702806, 0.0044868640855330435, 0.00220249367314024, 2.5247492892084583E-6, 0.22747159719051807, 0.14499131010569466, 0.0026349920559479525, 0.11076562221285752, 9.476362289942477E-6, 0.03170339386132826, 0.013405139748929224, 0.05837802079024489, 0.0030703285891893607, 0.002125880098441737, 0.002541401005347278, 0.045511221795755855, 0.006224997580328045, 0.02331759118929879, 0.03647961569651038, 1.2258500332038931E-5, 0.0026064315255786407, 0.007774541043833485, 0.0042904884517667545, 0.002003076688524196}, 
					{0.012434874711991926, 0.12268218956358308, 0.003113761639067872, 0.0993353556745467, 2.1793048164024315E-4, 0.037034559217597796, 0.01929284465215053, 0.00865796573688019, 5.964275254972731E-14, 0.2505138166858065, 0.10475383667514282, 0.0023978996455586397, 0.002147848544786943, 0.024338539969921146, 0.02477877229238882, 0.037079576504342876, 0.0017503162294806157, 1.1753052298381433E-4, 0.02237273886294996, 2.859358818596001E-9, 0.002933467966918218, 0.00116020010520244, 3.86302626937886E-4, 0.0016741444079913324, 0.05371925860815839, 0.004343037015558069, 0.0712899130247322, 0.004724032536916031, 5.775365058313057E-4, 0.032104792716236716, 0.05115756612461336, 2.105699080377416E-7, 2.877597568126481E-6, 0.0029062997231888905}
					};
		}
		else if(size==3){
			returnB = new double[][]{
					{0.009368789692847537, 0.05699892803271738, 0.0020344154537532814, 0.08216140618698893, 0.0016064210588629321, 0.015829823787962046, 0.02386228321766367, 0.03809270703816158, 0.0010041292340014162, 0.11775745418673243, 0.06084472123202605, 0.00204141953749271, 7.841017464475758E-4, 0.014501746885569902, 0.20957079495133435, 0.1440862033043383, 0.002910088055863232, 0.044764961519263645, 0.006903665235336774, 0.018532089481834598, 0.00695837204708259, 0.01889212922277404, 0.001957312373741304, 6.945645893990988E-4, 0.00898310896106793, 0.014463076915669674, 0.02869443654290884, 0.021656891916405647, 0.015033531936949734, 0.016948887477321796, 0.0018277176789535355, 0.0061920277494527335, 9.877413759378838E-4, 0.003054051373136994}, 
					{0.004317248605248768, 0.0626838122976682, 5.588389195300923E-4, 0.015334488668748439, 0.004123371564286356, 0.024892783177637944, 0.018522175893530425, 0.16787116851343212, 0.0020018406280493256, 0.04960067171696747, 0.02074159428843685, 0.013429198119091879, 0.008190867216480975, 0.018889792084937775, 0.07721914596336858, 0.017526979854187677, 6.449272011408941E-4, 0.06998797169615906, 0.011540346078648456, 0.045920448620108696, 0.010603199274656276, 0.0699618758110565, 0.002910125775361733, 0.001589042997149798, 0.031020319802028955, 0.08610347031217824, 0.08701032833168265, 0.009928084922322574, 0.008676785949501532, 0.0033733253247730005, 0.04931319916779475, 7.017657172789084E-4, 0.003772835572358204, 0.0010379699341966859}, 
					{0.0013527525280809902, 0.04633006462446406, 0.0013920611650543697, 0.0019124810670643254, 6.312244522343077E-4, 0.03918701637390138, 0.06689554085541026, 0.12216697842705437, 4.4278841040834354E-4, 0.11671493300529802, 0.08498468728896398, 0.0017344231579790679, 0.0015498094700832686, 8.916661493049525E-4, 0.08186009286822965, 0.07861096521359383, 0.0023824715308535887, 0.08691419541746977, 0.011787681608270631, 0.008005657536119407, 0.011136699398330972, 0.03961139525159245, 0.0015556900980267302, 0.0033915576052185666, 0.04020263351585577, 0.02281750494413081, 0.021404762127796118, 0.00966202711123094, 0.03532628629335816, 0.011820085233039537, 0.03790188781639386, 0.003682829934314612, 0.003863281375407922, 0.001875868145465164}
					};
		}
		else if(size==20){
				returnB = new double[][]{
				{0.009087447112447862, 2.157663376427611E-4, 0.003450230558390036, 0.014578438672927302, 6.537944263717917E-4, 0.048395533598529744, 8.423298777543236E-10, 7.895181195231801E-8, 1.7318695816316256E-24, 0.8374233570919177, 1.4490312095049287E-8, 4.8768342898325753E-17, 6.032138560684877E-30, 1.0369249930656764E-10, 1.9743536899425502E-9, 3.78234797903145E-7, 1.4918632445003114E-23, 2.1683108134996268E-13, 4.5758148070172975E-26, 4.211058866285729E-20, 1.3884022235048621E-24, 6.196810522950844E-7, 3.341898301004497E-4, 7.558918847869313E-4, 0.05614951274014773, 4.548172290210992E-6, 4.0592480141115223E-4, 0.01765936203056008, 3.5240706075682584E-4, 5.90169428986526E-6, 4.896618441966933E-4, 1.244577757749971E-4, 9.136180700833909E-4, 0.008998862018807393}, 
				{1.1078829280997665E-13, 1.6573929709316342E-15, 5.848058139944621E-10, 1.603889265960791E-5, 4.785414507503838E-7, 3.6700617234638725E-10, 0.0025591528915702676, 0.8623588898828864, 7.64309918007679E-71, 0.002696726099030925, 0.042277865671524355, 0.002704835232419995, 5.806197612998473E-7, 1.642460875436014E-16, 1.1873445781305754E-5, 3.345738725800653E-8, 3.252382132593433E-22, 4.644374727704546E-14, 3.639323216458504E-35, 2.124385856956579E-17, 0.07804584802244083, 2.8716816445864536E-5, 5.216770172619637E-6, 2.9697010670421053E-8, 5.559882681751937E-12, 5.428918897699133E-9, 8.819602359004431E-7, 0.004031682152483087, 0.0010731270323693825, 1.0626103909814524E-6, 1.832124244852712E-12, 0.0015228432629308354, 6.720263646275975E-4, 0.0019920841881380666}, 
				{0.004590512497137035, 0.003517711605894711, 0.0030830145372247653, 0.002000969308175348, 3.856497938151684E-4, 0.02812283600468522, 1.5217083361002665E-20, 4.96979712937116E-4, 1.7701463477836004E-39, 8.056430695637693E-5, 5.394208044531922E-12, 7.307187609439E-24, 8.70524405532989E-15, 0.001496416071661888, 0.0010384131109219758, 3.1518809798697036E-4, 1.0247073255455134E-32, 0.004772482572358682, 8.853724910251892E-5, 0.008605555773905897, 3.412206462969071E-30, 0.18491796395563934, 0.001344515325276324, 1.7260110919413373E-16, 0.7542068247138051, 1.7908205926637386E-12, 2.3916182495194592E-4, 4.1387376372281415E-6, 6.650237469083901E-9, 1.1071442313815715E-10, 6.924984282776477E-4, 5.842026706593885E-8, 1.1828960552590844E-9, 3.399506336401407E-13}, 
				{5.110485419817892E-4, 6.79773359751847E-4, 3.6739058979915357E-4, 1.2835003827203404E-4, 5.871207547234236E-10, 0.007301169175620165, 1.8914875166383583E-14, 2.211779051604385E-5, 1.1560443140985674E-37, 6.310129436550202E-5, 2.679380214764536E-13, 9.414986538445356E-13, 2.438878107629322E-14, 0.001643283561937547, 1.314069785665202E-10, 5.807058518748287E-11, 1.1521748381817586E-20, 1.203396470171203E-11, 1.368841682717509E-4, 2.740089827540817E-20, 1.667111405235469E-35, 0.00100256047930055, 0.0015561944422734534, 3.996744113357217E-20, 2.2177591150018614E-14, 5.3086904731862954E-5, 0.4763154343257676, 0.0019041809566652187, 1.5879517153265515E-5, 0.18968116528953727, 0.3186103377051051, 4.762488283909275E-7, 5.6735978698711994E-11, 7.56476347834429E-6}, 
				{8.508756422622624E-4, 4.197297866381504E-4, 1.006465718571801E-10, 1.4391341355477172E-4, 0.008362869429452332, 2.5970689175104264E-9, 0.1611584902382066, 5.068508407834716E-4, 1.321197723286892E-38, 3.522209776243232E-9, 1.10373964284198E-12, 1.7579756284464316E-5, 2.1568186607411235E-20, 3.6872549363736646E-5, 0.2473115285621013, 5.430422640296472E-4, 8.124356272289559E-4, 0.578827303103023, 4.105404460643523E-12, 1.3059959782452094E-7, 3.408253087808957E-51, 9.519760292424496E-4, 3.2660416298673935E-9, 7.609754076362605E-8, 6.606407339822087E-11, 5.079185583363542E-6, 5.121394981928879E-5, 2.2763014703105798E-12, 1.5363250442768464E-9, 1.7511296943088046E-13, 2.182926775706962E-8, 8.645450838691831E-23, 4.25048319620972E-18, 3.4188085188459946E-15}, 
				{6.159511135251245E-4, 0.014839877322547353, 5.1913071284932476E-8, 3.082990419442495E-4, 6.609985713770739E-7, 0.0022411136470007853, 4.440009451809999E-5, 9.147300926433688E-6, 2.0464165505393047E-19, 1.432027418776162E-4, 0.006779288570903262, 2.720233729671494E-4, 3.7160047865629794E-10, 7.061699086972512E-19, 0.03926122728941524, 3.0703111130128853E-7, 2.9289163516681653E-4, 0.9212813571377788, 1.3760202975869368E-21, 1.2236989080994198E-5, 4.3493670388191875E-45, 3.967276565249775E-4, 1.718807411882165E-6, 5.426222416638351E-4, 1.3114761103536439E-18, 3.6426521801713912E-9, 5.745926411542295E-11, 0.0026306323024693984, 0.0013177614012869314, 6.0450378639729094E-5, 1.3367139813704973E-6, 7.559732676467387E-4, 0.008190736958257122, 3.583404123227926E-16}, 
				{0.03357918133993598, 0.45762186651409015, 0.0028344295533020807, 0.4943367587124604, 5.842906868538955E-4, 9.890588950961529E-5, 4.4766218831287503E-13, 1.258177647823064E-10, 8.206050414256579E-36, 0.005180885750007656, 3.506657377669324E-5, 3.7480384484829124E-14, 3.798120456297275E-27, 5.357232305825015E-18, 5.925776912930223E-4, 2.3575206114685763E-6, 7.495639327230782E-12, 7.904183849693805E-6, 1.3258460420640891E-19, 1.3728088116040136E-20, 1.8581770121946143E-29, 2.527149408398276E-7, 4.670421097635883E-8, 8.432801501955557E-12, 4.000813468894783E-6, 7.131942664422622E-10, 5.174938145941042E-5, 1.8896623259630175E-5, 7.55328871467145E-5, 6.350871385211088E-8, 1.1228994291340855E-7, 6.951322329523382E-8, 1.4875771845879402E-4, 0.004826292574058036}, 
				{0.004219600411979049, 1.5118547199976216E-4, 2.9091710935212373E-7, 0.0017930778512671036, 1.4903013794926166E-4, 0.036834122817908675, 8.295745037744078E-28, 7.328151527735966E-4, 4.0774411132249437E-69, 1.7227607469503949E-10, 9.435996828967834E-19, 2.654195082630654E-26, 4.787963753811745E-33, 0.8963639070095367, 1.4615286092088713E-9, 8.608028226452543E-13, 1.0054329824373199E-10, 2.4960464948172964E-6, 3.8442674809638635E-12, 6.133418159051836E-18, 1.8259114143124446E-65, 4.946290749759422E-6, 7.938137630817074E-13, 3.405329506344331E-27, 0.05284511913933266, 4.090238144033941E-7, 0.004381215031957707, 2.245907392544698E-14, 2.886427413760229E-19, 3.2819980823212096E-9, 0.001123475386128926, 7.451554202225812E-4, 6.531488294583679E-4, 3.944974464557075E-11}, 
				{0.02410244603881942, 0.22649299342015275, 0.008192482674495287, 0.06542524065125498, 2.0243043504492028E-4, 0.21134110330451175, 1.2818028524049422E-9, 4.51487005385885E-9, 2.5701557098469815E-42, 0.21015507570317904, 8.807208611809982E-8, 1.2562134937102575E-24, 3.44765436644658E-37, 3.067072961457701E-32, 8.267375509602881E-6, 1.5466687142353647E-4, 6.751031810650191E-9, 1.414306778650466E-10, 0.21997317286332352, 1.2268232698359412E-27, 2.856179864065536E-22, 3.706743039006429E-12, 2.1378739176074134E-8, 5.957912761771344E-22, 0.006128897178553096, 1.5084137568842388E-8, 0.025249984417269163, 0.0021132153280510376, 3.576657578924751E-4, 6.705919666688167E-8, 9.303521728348288E-5, 2.518751866648339E-12, 1.4421491972260574E-14, 9.118473701289596E-6}, 
				{2.562923880290913E-5, 2.8841981843561224E-5, 3.408057620935924E-5, 1.6683979465415418E-6, 2.0771189426275438E-5, 8.327945410713946E-8, 7.539264498413336E-6, 1.2175151401759186E-5, 2.679354986853631E-41, 4.89957554495053E-5, 8.772193753510248E-13, 4.996556890102357E-10, 7.442407951895535E-24, 4.0003320980346534E-16, 3.797535509487101E-5, 1.5891864718384753E-7, 1.0312768484932865E-38, 3.846262512746433E-9, 2.4488637030281E-43, 0.0017164983755981662, 4.617657645217632E-19, 0.003804574514358286, 0.0012108268519594838, 2.772605878344542E-29, 2.0855675712611486E-22, 0.9923186120898227, 1.632373960333727E-4, 3.0905181430942904E-4, 2.013630439104849E-10, 2.6224703243306394E-5, 3.200925021955011E-7, 2.922646666506976E-8, 4.43989859459458E-5, 1.883022928270939E-4}, 
				{0.0011881873343725985, 2.7808029069360884E-8, 0.005464425493402127, 4.3293167816654686E-8, 0.006161585467748195, 0.3114342364030323, 0.026976221879828254, 0.001226781684470935, 4.077004814510526E-32, 0.0034243145623026904, 3.3591634471165756E-4, 0.0010908440962438344, 0.01443322639661079, 0.002568166321242388, 9.494746933245834E-4, 3.1532203533300594E-5, 1.0126788365259596E-11, 0.04482484746260486, 2.946978781043176E-26, 1.4992478866567962E-4, 3.737500078319014E-24, 0.3943386572109723, 0.008180701727224649, 3.8443657105098686E-13, 4.034160221082185E-13, 2.052441398595819E-10, 7.517916028118444E-7, 0.14285509564770535, 0.005044423791655156, 6.979466891716988E-11, 3.081914333960661E-5, 8.584396197234757E-4, 0.002891330897743524, 0.025540023650788442}, 
				{2.4774747493804905E-4, 9.498475895826238E-6, 7.629163527621967E-5, 0.0015661770766406359, 5.965726251540229E-9, 3.729087113453201E-8, 0.0062605851607141095, 0.005923867426071689, 4.3806663364081677E-13, 0.033212618172533195, 0.10357047403817514, 0.012720527306700358, 7.07550478257798E-4, 2.8909219095721767E-12, 0.01607356001553652, 0.0041427004144490055, 7.82001378855234E-13, 0.0460754114253548, 5.962401200982999E-26, 0.036937041414453614, 2.2517471074814098E-25, 0.27642935442595223, 0.01878792758140379, 0.02875318168049443, 1.833174552811028E-4, 2.8640390621636574E-4, 0.0021661333175546312, 0.08613044803613161, 0.3183831000996774, 4.0643142390510663E-7, 8.43835594918403E-6, 5.160193539520201E-19, 3.737333127777032E-4, 9.734616214322404E-4}, 
				{0.0118062127622213, 6.165562986964032E-6, 6.393003714868612E-4, 0.0021014936386345366, 0.007945688888124759, 0.009323903680518048, 1.793573112107797E-6, 5.195382238117164E-4, 3.1941017617582577E-38, 0.007979171912302829, 0.35512309820711835, 0.06930318411481769, 4.0674518745461865E-6, 6.705775679176581E-19, 0.40174335055974447, 3.5964308071584865E-4, 8.57890189152608E-12, 0.01841603172242145, 3.1319126109764865E-11, 1.1270945590707781E-11, 8.876612721632845E-19, 0.005800478336330739, 0.027687027326023995, 1.292564595404215E-4, 1.307575050357621E-9, 2.4111510057594305E-9, 6.445628069728938E-5, 0.002536719090685457, 0.0784736644453581, 5.1599041653855036E-9, 3.140124811134451E-5, 1.1209109919250547E-9, 1.3067566986723622E-10, 4.342881975505701E-6}, 
				{3.639291859925567E-4, 5.648171098552751E-4, 0.002876045516581434, 2.9341693986490435E-5, 0.004237496047409531, 0.008310595363021398, 0.00886433246537473, 5.933576012529766E-4, 9.995694261489748E-27, 1.1154748993995108E-4, 0.0013011261739702107, 7.330146857106142E-17, 1.75408827879632E-20, 3.88947510852205E-7, 0.010287902775632568, 0.9354628050600363, 0.021029567374244065, 2.1770201727672475E-6, 0.0017283295715586448, 1.21123934875789E-4, 1.4021257805998125E-26, 3.2689753455565293E-4, 5.805497695470295E-19, 7.170479468239571E-19, 3.809818121048822E-16, 3.140174506276974E-7, 8.123843688987539E-4, 3.2484972837036266E-5, 1.1316362900427156E-4, 4.4072283685509147E-14, 0.0022121205963868537, 5.347262981825054E-4, 2.444695482719834E-6, 8.058055574119015E-5}, 
				{2.2085120587984949E-16, 4.936038682620898E-10, 2.2414787415480207E-7, 3.560490257966698E-7, 3.4702575464813074E-14, 8.310693562533892E-15, 0.5299735116631956, 0.01558460039860181, 1.1603706221362334E-17, 0.005025516525149733, 0.07928002527618223, 0.013737290217584117, 0.026623122273681105, 1.7289999497865663E-11, 0.0051492842181810215, 0.028702554783068136, 4.1988636699293614E-11, 0.0023807244064505406, 1.0876706098652955E-30, 0.014536475951459803, 0.005726060711811649, 0.011974880660486274, 4.4919937762247545E-13, 1.8635564213943372E-8, 4.361887074028333E-9, 2.3800866327701987E-4, 9.732647169785226E-4, 0.22645920114923512, 0.020543104263584612, 1.4517171107021136E-9, 1.4191359954915795E-11, 0.008389358705263434, 0.0037211180232634504, 9.812921789108969E-4}, 
				{5.475159401789935E-4, 1.0282594365923914E-5, 1.1920476735469E-10, 1.5694645116974962E-14, 7.228040942456566E-4, 0.028952946830321506, 5.218313121934163E-6, 0.052122047644986555, 0.03510547417215191, 0.0018846940221163586, 0.0050302556802869535, 3.71713573815962E-4, 0.001119515647223617, 1.4661714750539552E-8, 0.0238475248644552, 0.0025257513370758647, 5.797065312552218E-10, 0.10706280887178402, 1.4852656827169953E-27, 0.5778759990825268, 2.8655899140444487E-46, 0.025709811728791717, 3.989234407560033E-6, 1.0308204936311852E-19, 1.156732765676843E-38, 4.5779019530499973E-11, 4.180489093869626E-14, 7.840212047344616E-4, 5.0387985624127206E-5, 7.186176575904919E-10, 7.150996184431693E-15, 0.0835101453740902, 0.048578627861985, 0.004178447816622268}, 
				{1.8547148803107073E-7, 1.5281896456641582E-13, 2.0300292103763464E-7, 5.3391136777225216E-11, 2.50400057708452E-13, 1.0006153985108678E-15, 5.422523465469383E-5, 2.435471027201328E-4, 2.2629688734297717E-37, 3.201555046112011E-12, 3.136037054881938E-4, 2.6525318537444922E-6, 0.0011316381792952906, 2.342766278644021E-24, 0.8888127363891172, 0.003068467252651574, 0.0016754252347815086, 0.10462886354473028, 4.114349785301756E-23, 6.789431630389395E-8, 7.241986743802897E-30, 1.7391567424358845E-11, 6.6233944104657E-10, 3.968866299816302E-5, 1.0020066724190455E-26, 5.588597129700647E-14, 5.684004676724699E-12, 1.136279382197047E-5, 1.8705932783458466E-7, 1.1093933314391985E-10, 1.7145085096078617E-5, 1.0364491044850424E-18, 1.3303116720594424E-12, 2.806248266725129E-26}, 
				{3.7093177760652126E-10, 4.584372924920427E-9, 4.746295864334315E-6, 6.832519134202769E-7, 1.2250310833585015E-4, 8.928384882556123E-7, 0.3454495085789274, 1.2837422416742131E-5, 9.422792643716424E-56, 6.037755407371381E-4, 0.3211848062127396, 0.014917332400930067, 6.493427294545987E-5, 1.166773990045402E-17, 0.22659116681969166, 0.0484513523644034, 1.2661258808954653E-9, 0.00244694758172142, 1.0604892150360146E-22, 5.291565023195269E-13, 0.02113507249167876, 4.3654543645125224E-7, 1.5127368597038362E-20, 0.0017572594306690796, 0.005651441606032507, 5.398232717685536E-10, 1.0783516429043761E-5, 0.0019172440969819867, 0.009676268313384489, 5.472194513665177E-10, 1.703240778345028E-15, 1.2919377120484073E-13, 1.0742528931906266E-12, 6.538657620364484E-14}, 
				{1.1015002421740014E-16, 9.596594671034967E-15, 4.576432995626968E-4, 1.1826260745333412E-8, 7.533138782734566E-5, 7.44904490948779E-9, 0.08162239701229328, 4.026594859796904E-4, 5.830029995234588E-87, 1.7619608101126951E-12, 0.6849336056745098, 0.014259169209331079, 0.025174286087508116, 3.101956986919542E-23, 0.03178574345086588, 0.10791862899574567, 1.965553903961073E-14, 2.36291467027287E-10, 1.7605781988821345E-25, 6.679320549367918E-29, 9.917656078237838E-4, 0.002131073022258897, 0.002160547825178076, 1.936590938956069E-17, 8.018798062996582E-22, 2.011355422480544E-13, 6.436742921461905E-9, 0.020937695500946967, 0.026508931362507277, 1.9540217895989414E-4, 4.171715837347481E-4, 5.572414847052964E-23, 2.7922364634681893E-5, 3.723973157888167E-23}, 
				{0.01943682442210814, 0.4656428351211003, 1.1231661501059765E-9, 6.965580873324065E-4, 0.010401606320249895, 0.08983702219976905, 1.767793262371494E-8, 1.491404354538899E-7, 2.6459564686957545E-20, 2.0089336202821805E-6, 4.2216195987149776E-10, 3.768544877282251E-8, 1.9389282970278018E-21, 2.6055127122815994E-8, 0.008803482754465904, 2.5695513065047635E-5, 2.6573163363256083E-8, 0.03424866275687753, 3.289663948895726E-5, 5.793964835636013E-11, 3.59580537190798E-44, 0.2301029476561191, 0.001890564209093439, 6.275735502041223E-13, 2.6439959453366745E-22, 1.3165615507212178E-12, 8.187816745483178E-7, 2.3257322217562928E-8, 0.0011910422454201855, 8.428977346948148E-6, 1.0172006067225048E-4, 0.09938291319438611, 0.03819369013256841, 1.2562562767829727E-17}
				};
		}
		if(returnB!=null){
			if(returnB.length!=size){
				throw new RuntimeException("length of returnA("+returnB.length
						+") differnent from size input("+size+")");
			}
		}
		return returnB;
	}
	
	public static HMM create(int N){
		double[][] tmpA = getInitialA(N);
		double[][] tmpB = getInitialB(N);
		
		if(tmpA==null || tmpB==null){
			return new HMM(N, WordType.size());
		}
		else{ return new HMM(tmpA, tmpB); }
	}
	
	private int startIndex = 0;
	private double threshold = 0.001;
	
	
	private int getStartIndex(){ return startIndex; }
	private int getEndIndex(){ return this.getStateCount()-1; }
	private double getThreshold(){ return threshold; }
	
	public double[] initLogAlphaColumn(){
		return initLogCoeffs(this.getStartIndex());
	}
	public double[] initLogBetaColumn(){
		return initLogCoeffs(this.getEndIndex());
	}
	
	
	protected double[] initLogCoeffs(int indexOfOne){
		double [] init = new double[this.getStateCount()];
		Arrays.fill(init, Double.NEGATIVE_INFINITY);
		init[indexOfOne]=0;
		return init;
	}
	
	/*public static int getCharacterIndex(char c){
		if(c==' '){ return 0; }
		else{ return c-'A'+1; }
	}*/
	
	private static void checkA(double[][] a){
		int length = a.length;
		for(int i=0; i<a.length; i++){
			double[] d = a[i];
			if(d.length!=length){ throw new RuntimeException("Length Exception @ index "+i); }
		}
	}
	private static void checkB(double[][] b){
		int length = b[0].length;
		for(int i=0; i<b.length; i++){
			double[] d = b[i];
			if(d.length!=length){ throw new RuntimeException("Length Exception @ index "+i); }
		}
	}
	
	private static void checkArrays(double[][] a, double[][] b){
		checkA(a);
		checkB(b);
		if(a.length!=b.length){
			throw new RuntimeException("State number inconsistent!");
		}
		
	}
	
	public static double[][] copyOf(double[][] in){
		double[][] out = new double[in.length][];
		for(int i=0; i<in.length; i++){
			out[i] = Arrays.copyOf(in[i], in[i].length);
		}
		return out;
	}
	
	public static void normalize(double[] ds){
		double sum = 0;
		for(double d : ds){ sum += d; }
		for(int i=0; i<ds.length; i++){ ds[i] /= sum; }		
	}
	
	
	private HMM(int N, int L){
		a = new double[N][N];
		b = new double[N][L];
		
		Random r = new Random();
		for(int i=0; i<N; i++){
			double sum = 0;
			for(int j=0; j<N; j++){
				double d = r.nextDouble();
				a[i][j] = d;
				sum += d;
			}
			normalize(a[i]);
			
			for(int k=0; k<L; k++){
				double d = r.nextDouble();
				b[i][k] = d;
				sum += d;
			}
			normalize(b[i]);
		}
	}
	
	private HMM(double[][] a, double[][] b){
		checkArrays(a,b);
		this.a = copyOf(a);
		this.b = copyOf(b);
	}
	
	private HMM(HMM hmm){
		this(hmm.a, hmm.b);
	}
	
	
//	public double a(int from, int to){ return a[from][to]; }
	public double b(int s, EmissionCharacter c){ return b[s][c.getIndex()]; }
	
	/*public static int getStateSize(){
		return 2;
		// return a.length;
	}
	public static int getSymbolSize(){
		return 27;
		// return b[0].length;
	}*/
	
	transient private double[][] logAlpha;
	transient private double[][] logBeta;
	
//	public double[] getLogAlphaColumn(int t){ return logAlpha[t]; }
//	public double[] getLogBetaColumn(int t){ return logBeta[t]; }
	
	/*protected SmallCoefficients nextViterbiColumn(SmallCoefficients prevViterbiColumn, EmissionCharacter o_t){
		SmallCoefficients viterbiColumn = new SmallCoefficients(prevViterbiColumn.size(), prevViterbiColumn.getNormalizer());
		//int[] states = new int[getStateSize()];
		
		int N = this.getStateCount();
		for(int j=0; j<N; j++){
			double max = 0;
			double current = 0;
			for(int i=0; i<N; i++){
				current = prevViterbiColumn.getRelativeCoeff(i) * this.a(i, j);
				if(current>max){
					max = current;
					viterbiColumn.setPrevState(j, i);
				}
			}
			viterbiColumn.setRelativeCoeff(j, max*this.b(j, o_t));
		}
		viterbiColumn.normalize();

		return viterbiColumn;
	}
	
	public SmallCoefficients[] buildViterbi(EmissionCharacter[] output){
		
		SmallCoefficients viterbiColumn = initAlphaColumn();	
		int T = output.length;
		SmallCoefficients[] viterbiMatrix = new SmallCoefficients[T+1];
		viterbiMatrix[0] = viterbiColumn;
		for(int t=0; t<T; t++){
			viterbiColumn = nextViterbiColumn(viterbiColumn, output[t]);
			viterbiMatrix[t+1] = viterbiColumn;
		}
		return viterbiMatrix;
	}*/
	
	protected double[] nextLogAlphaColumn(double[] prevLogAlphaColumn, EmissionCharacter o_t){
		double[] logAlphaColumn = new double[prevLogAlphaColumn.length];
		int N = this.getStateCount();
		
		for(int j=0; j<N; j++){
			double logSum = Double.NEGATIVE_INFINITY;
			for(int i=0; i<N; i++){
				double a = this.a[i][j];
				double logA = Math.log(a);
				double tmp = prevLogAlphaColumn[i]+logA;
				logSum = Logdouble.add(tmp, logSum);
			}
			logAlphaColumn[j] = logSum+Math.log(this.b(j, o_t));
		}
		return logAlphaColumn;
	}
	
	public double[][] buildLogAlpha(EmissionCharacter[] output){
		int T = output.length;
		
		double[][] newLogAlpha = new double[T+1][];
		newLogAlpha[0] = initLogAlphaColumn();
		
		for(int t=0; t<T; t++){	
			newLogAlpha[t+1] = nextLogAlphaColumn(newLogAlpha[t], output[t]);
		}
		return newLogAlpha;
	}
	
	protected double[] prevLogBetaColumn(double[] postLogBetaColumn, EmissionCharacter o_t){		
		double[] betaLogColumn = new double[postLogBetaColumn.length];
		int N = this.getStateCount();
		for(int i=0; i<N; i++){
			double logSum = Double.NEGATIVE_INFINITY;
			for(int j=0; j<N; j++){
				double tmp = Math.log(this.a[i][j])+Math.log(this.b(j, o_t))+postLogBetaColumn[j];
				logSum = Logdouble.add(logSum, tmp); 
			}
			betaLogColumn[i] = logSum;
		}
		return betaLogColumn;
	}
	
	public double[][] buildLogBeta(EmissionCharacter[] output){
//		double[] logBetaColumn = initLogBetaColumn();
		int T = output.length;
		double[][] newLogBeta = new double[T+1][];
		
		newLogBeta[T] = initLogBetaColumn();
		for(int t = T-1; t>=0; t--){
			newLogBeta[t] = prevLogBetaColumn(newLogBeta[t+1], output[t]);
		}
		return newLogBeta;
	}
	
	
	protected void buildGammaAndUpdate(EmissionCharacter[] output){
		int T = this.logAlpha.length-1;
		int N = this.getStateCount();
		int L = this.getSymbolCount();
		
		
		double[][] newA = new double[N][N];
		double[] tmpASum = new double[N];
		
		double[][] newB = new double[N][L];
			
		for(int i=0; i<N; i++){
			tmpASum[i] = 0;
			
			for(int j=0; j<N; j++){
				newA[i][j] = 0;
				if(i==0){ Arrays.fill(newB[j],0); }
				
				
				for(int t=0; t<T; t++){
					double logItem = this.logAlpha[t][i];
					EmissionCharacter o_t_1 = output[t];
					logItem += Math.log(this.a[i][j]);
					logItem += Math.log(this.b(j, o_t_1));
					logItem += this.logBeta[t+1][j];
					logItem -= this.getBetaLogLikelihood(Math.E);
					double item = Math.pow(Math.E, logItem);
					
					int charIndex = output[t].getIndex();
					newA[i][j] += item;
					newB[j][charIndex] += item;
				}
				tmpASum[i] += newA[i][j];
			}
			
			for(int j=0; j<N; j++){
				newA[i][j] = newA[i][j]/tmpASum[i];
			}
			check(newA[i]);
		}
		this.a = newA;
		
		for(int j=0; j<N; j++){
			double tmpBSum = 0;
			for(int k=0; k<L; k++){ tmpBSum += newB[j][k]; }
			
			for(int k=0; k<L; k++){
				newB[j][k] = newB[j][k]/tmpBSum;
			}
			check(newB[j]);
		}
		this.b = newB;
	}
	
	
	public void check(double[] probs){
		double sum = 0;
		for(double prob : probs){ sum += prob; }
		if( Math.abs(sum-1)>this.getThreshold() ){ throw new RuntimeException("Probs doesn't some to 1!"); }
	}
	
	/*public double getAlphaLogLikelihood(double base){
		return this.logAlpha[this.logAlpha.length-1][this.getEndIndex()]/Math.log(base);
	}*/
	public double getBetaLogLikelihood(double base){
		return this.logBeta[0][this.getStartIndex()]/Math.log(base);
	}
	
	public void buildBetaAndUpdate(EmissionCharacter[] output){
		this.logBeta = this.buildLogBeta(output);
		this.buildGammaAndUpdate(output);
	}
	/*
	public void buildGreekAndUpdate(EmissionCharacter[] output){
		this.logAlpha = this.buildLogAlpha(output);		
		this.buildBetaAndUpdate(output);
	}*/
	
	private int getStateCount(){ return this.a.length; }
	private int getSymbolCount(){ return this.b[0].length; }
	
	public String getDescription() {
		return "HMM (State#="+this.getStateCount()+")";
	}
	
	transient private double[] logKnownAlpha = null;
	public double[] predict(WordType wordType) {
		if(wordType==null){
			// prepare for prediction
			this.logKnownAlpha = this.initLogAlphaColumn();
		}else if(this.logKnownAlpha==null){
				throw new RuntimeException(
						"logKnownAlpha==null! The first input for the predict() should be null! current wordType="+wordType.getOriginalTag());
		}else{
			logKnownAlpha = this.nextLogAlphaColumn(logKnownAlpha, wordType);
		}
		double[] logPrediction = new double[WordType.size()];
		
		for(WordType nextPossibleWord : WordType.values()){
			double[] logGuessingAlpha = this.nextLogAlphaColumn(logKnownAlpha, nextPossibleWord);
			double logSum = Double.NEGATIVE_INFINITY;
			for(double d : logGuessingAlpha){
				logSum = Logdouble.add(logSum, d);
			}
			logPrediction[nextPossibleWord.getIndex()] = logSum;
		}
		return Logdouble.normalizedProb(logPrediction);
	}
	public void prepare(WordType[] allWords){}
	
	private double getAverageLogAlphaLikelihood(int trainLength){
		return logAlpha[logAlpha.length-1][this.getEndIndex()]/Math.log(2)/trainLength;
	}
	public void train(TrainTokens trainTokens) {
		WordType[] tt = trainTokens.getTokenArray();
		int index = 0;
		
		this.logAlpha = this.buildLogAlpha(tt);
		double avgLogLikelihood = Double.NEGATIVE_INFINITY;
		double newAvgLogLikelihood = getAverageLogAlphaLikelihood(tt.length);
		Global.debugOut.println("Normalized Log-likelihood @ iteration "
				+index+": train= "+newAvgLogLikelihood);
		do{
			index++;
			avgLogLikelihood = newAvgLogLikelihood;
			this.buildBetaAndUpdate(tt);
			
			double[][] trainLogAlpha = this.buildLogAlpha(tt);
			newAvgLogLikelihood = trainLogAlpha[trainLogAlpha.length-1][this.getEndIndex()]/Math.log(2)/tt.length;
			
			this.logAlpha = this.buildLogAlpha(tt);
			this.getAverageLogAlphaLikelihood(tt.length);
			Global.debugOut.println("Normalized Log-likelihood @ iteration "
					+index+": train= "+newAvgLogLikelihood);
			
		}while(Math.abs(avgLogLikelihood-newAvgLogLikelihood) > this.threshold
				&& avgLogLikelihood<newAvgLogLikelihood);
		
		// print parameters
		Global.debugOut.println("Printing parameter settings!");
		Global.debugOut.println("a = "+getParameterString(this.a));
		Global.debugOut.println("b = "+getParameterString(this.b));
		
		this.logAlpha = null;
		this.logBeta = null;
	}
	private static String getParameterString(double[][] dAA){
		StringBuffer buffer = new StringBuffer();
		String newLine = System.getProperty("line.separator");
		buffer.append("new double[][]{").append(newLine);
		for(int i=0; i<dAA.length; i++){
			double[] dA = dAA[i];
			
			buffer.append('{');
			for(int j=0; j<dA.length; j++){
				if(j!=0){ buffer.append(", "); }
				buffer.append(dA[j]);
			}
			buffer.append('}');
			if(i!=dAA.length-1){ buffer.append(", "); }
			buffer.append(newLine);
		}
		buffer.append("};");
		return buffer.toString();
	}
	public void setThreshold(double threshold) {
		this.threshold = threshold;
	}
	
	public LanguageModel clone(){ return new HMM(this); }
	
	public static List<LanguageModel> createModel(int i){
		List<LanguageModel> models = new ArrayList<LanguageModel>();
		models.add(new HMM(i, WordType.size()));
		return models;
	}
	
	
	public static void main(String[] args) throws IOException{
		long start = System.currentTimeMillis();
		
		run02(args);
//		HMM model = new HMM(3, WordType.size());
//		System.out.println(model.getSaveFileName(new GregorianCalendar()));
		
		long end = System.currentTimeMillis();	
		Global.debugOut.println("Program ended normally! in " +((end-start)/1000/60)+" minutes!" );
	}
	
	public String getSaveFileName(Calendar cal){
		DateFormat format = new SimpleDateFormat("yyyyMMdd_HHmmss");
		return format.format(cal.getTime())+"_HMM("+this.getStateCount()+")";
	}
	
	public static void run02(String[] args) throws IOException{
		Global.debugOut.println("Starting JLMTools...");
	
		Global.debugOut.println("Preparing training-evaluation set...");
		WordType[] trainATokens = WordType.parse(Global.getTrainA());
		
//		int[] sizeArray = new int[args.length];
//		for(int i=0; i<args.length; i++){
			int i=0;
			int stateSize = Integer.parseInt(args[i]);
//			sizeArray[i] = stateSize; 
			
			
			Global.debugOut.println("Creating HMM ("+stateSize+")...");
			HMM model = HMM.create(stateSize);
			
			WordType[][] tokens = CrossValidationTokenSet.divide(trainATokens, 10);
			
			WordType[] trainTokens = tokens[0];
			WordType[] evalTokens = tokens[1];				
		
			Global.debugOut.println("Starting training model...");
			model.train(new TrainTokens(trainTokens));
//		}
		
		
		
		
		
		Global.debugOut.println("Calculate Average-Log-Likelihood...");
		CrossValidationResultItem item =
			CrossValidationResultItem.evaluate(model, evalTokens);
		
		File ofile = new File(Global.modelsFolder,
				model.getSaveFileName(new GregorianCalendar())+"."+Global.modelFileExtension);
		Global.debugOut.println("Saving to file... "+ofile.getName());
		Global.save(model, ofile);
		
		Global.debugOut.println(item.makeReport());
	}
	public static void run01(String[] args) throws IOException{
		Global.debugOut.println("Preparing training-evaluation set...");
		WordType[] trainATokens = WordType.parse(Global.getTrainA());
		
//			System.out.println(trainATokens.length);
		CrossValidationTokenSet cvts = new CrossValidationTokenSet(trainATokens, 10);
		
		int stateSize = Integer.parseInt(args[0]);
		Global.debugOut.println("Creating HMM ("+stateSize+")...");
		LanguageModel model = new HMM(stateSize, WordType.size());
		
		Global.debugOut.println("Starting Cross-validation("+cvts.getValidationSize()+")...");
		CrossValidationResult result = cvts.crossValidate(model);
		Global.debugOut.println(result.makeReport());
	}
}
